﻿using System;
using System.Collections.Specialized;
using System.Linq;
using System.Net;
using System.Collections.Generic;

namespace MyCollections
{
    /// <summary>
    /// ObservableDictionary extends the standard Dictionary class by 
    /// providing notifications when the collection changes
    /// Note: Move action is not implemented
    /// Reason: this was developed for Silverlight application
    /// http://msdn.microsoft.com/en-us/library/system.collections.specialized.notifycollectionchangedaction%28v=vs.95%29.aspx
    /// </summary>
    /// <typeparam name="TKey">Key</typeparam>
    /// <typeparam name="TValue">Value</typeparam>
    public class ObservableDictionary<TKey, TValue> : Dictionary<TKey, TValue>,INotifyCollectionChanged
    {
        public ObservableDictionary() : base() { }
        public ObservableDictionary(int capacity) : base(capacity) { }
        public ObservableDictionary(IEqualityComparer<TKey> comparer) : base(comparer) { }
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary) : base(dictionary) { }
        public ObservableDictionary(int capacity, IEqualityComparer<TKey> comparer) : base(capacity, comparer) { }
        public ObservableDictionary(IDictionary<TKey, TValue> dictionary, IEqualityComparer<TKey> comparer) : base(dictionary, comparer) { }

        public event NotifyCollectionChangedEventHandler CollectionChanged;

        /// <summary>
        /// Gets/Sets the value for the specified key.
        /// </summary>
        /// <param name="key">Key</param>
        /// <returns>Value</returns>
        public new TValue this[TKey key]
        {
            get
            {
                return base[key];
            }
            set
            {
                var olditems = new object[] { key, this[key] };
                var newitems = new object[] { key, value };

                base[key] = value;
                
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Replace, newitems, olditems));
                
            }
        }

        /// <summary>
        /// Adds a new Key-Value pair to the dictionary if the key doesn't exists on the 
        /// dictionary
        /// </summary>
        /// <param name="key"></param>
        /// <param name="value"></param>
        public new void Add(TKey key, TValue value)
        {

            if (!TryGetKey(key))
            {

                base.Add(key, value);
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Add, new object[] { key, this[key] }));
        }
        


        /// <summary>
        /// Removes the specified key, if the key exists .
        /// </summary>
        /// <param name="key">The key to be removed</param>
        /// <returns>True if the key exists, false otherwise</returns>
        public new bool Remove(TKey key)
        {
            if (TryGetKey(key))
            {
                OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Remove, new object[] { key, this[key] }));
                return base.Remove(key);
            }
            return false;
        }

        /// <summary>
        /// Attemps to find the specified key.
        /// </summary>
        /// <param name="key">The key to search for</param>
        /// <returns>TRue if key exists, false otherwise</returns>
        private bool TryGetKey(TKey key)
        {
            return this.Any(pair => pair.Key.Equals(key));
        }

        /// <summary>
        /// Clears the dictionary
        /// </summary>
        public new void Clear()
        {
            OnCollectionChanged(new NotifyCollectionChangedEventArgs(NotifyCollectionChangedAction.Reset));
            base.Clear();
        }

        /// <summary>
        /// Handles the collection altered event
        /// </summary>
        /// <param name="e"></param>
        protected virtual void OnCollectionChanged(NotifyCollectionChangedEventArgs e)
        {
            if (CollectionChanged != null)
            {
                CollectionChanged(this, e);
            }
        }


        
    }
}
